#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
#include "util-cjson.h"
#include "debug.h"
#include "common.h"

char *g_strdup(const char *in)
{
    int len;
    char *p;

    if (in == 0)
    {
        return 0;
    }

    len = strlen(in);
    p = (char *)malloc(len + 1);

    if (p != NULL)
    {
        strcpy(p, in);
    }

    return p;
}

int g_directory_exist(const char *dirname)
{
#if defined(_WIN32)
    return 0; // use GetFileAttributes and check return value
    // is not -1 and FILE_ATTRIBUTE_DIRECTORY bit is set
#else
    struct stat st;

    if (stat(dirname, &st) == 0)
    {
        return S_ISDIR(st.st_mode);
    }
    else
    {
        return 0;
    }

#endif
}

int g_create_dir(const char *dirname)
{
#if defined(_WIN32)
    return CreateDirectoryA(dirname, 0); // test this
#else
    return mkdir(dirname, (mode_t)-1) == 0;
#endif
}


/*
这个函数用于判断path路径是否存在，可以处理递归创建文件目录以及文件
*/
int g_create_path(const char *path)
{
    char *pp;
    char *sp;
    char *copypath;
    int status;

    status = 1;
    copypath = g_strdup(path);
	// "/var/run/kvm_manage.pid"
    pp = copypath;
    sp = strchr(pp, '/');

    while (sp != 0)
    {
        if (sp != pp)
        {
            *sp = 0;

            if (!g_directory_exist(copypath))
            {
                if (!g_create_dir(copypath))
                {
                    status = 0;
                    break;
                }
            }

            *sp = '/';
        }

        pp = sp + 1;
        sp = strchr(pp, '/');
    }

    free(copypath);
    return status;
}

int g_getpid(void)
{
#if defined(_WIN32)
    return (int)GetCurrentProcessId();
#else
    return (int)getpid();
#endif
}

int g_file_exist(const char *filename)
{
#if defined(_WIN32)
    return 0; // use FileAge(filename) <> -1
#else
    return access(filename, F_OK) == 0;
#endif
}

int g_file_write(int fd, const char *ptr, int len)
{
#if defined(_WIN32)

    if (WriteFile((HANDLE)fd, (LPVOID)ptr, (DWORD)len, (LPDWORD)&len, 0))
    {
        return len;
    }
    else
    {
        return -1;
    }

#else
    return write(fd, ptr, len);
#endif
}

int g_file_read(int fd, char *ptr, int len)
{
#if defined(_WIN32)

    if (ReadFile((HANDLE)fd, (LPVOID)ptr, (DWORD)len, (LPDWORD)&len, 0))
    {
        return len;
    }
    else
    {
        return -1;
    }

#else
    return read(fd, ptr, len);
#endif
}

int g_file_close(int fd)
{
#if defined(_WIN32)
    CloseHandle((HANDLE)fd);
#else
    close(fd);
#endif
    return 0;
}

int g_file_open(const char *file_name)
{
#if defined(_WIN32)
    return (int)CreateFileA(file_name, GENERIC_READ | GENERIC_WRITE,
                            FILE_SHARE_READ | FILE_SHARE_WRITE,
                            0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
#else
    int rv;

    rv = open(file_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

    if (rv == -1)
    {
        rv = open(file_name, O_RDONLY);
    }

    return rv;
#endif
}

int redirect_fd(int fd, int flags)
{
    int new_fd = open("/dev/null", flags);
    if (new_fd < 0)
    {
        return 1;
    }

    if (new_fd != fd)
    {
        dup2(new_fd, fd);
        close(new_fd);
    }

    return 0;
}

int daemonize()
{
    pid_t pid;

    pid = fork();
    if (pid < 0)
    {
        printf("Could not fork() parent: %d\n", errno);
        return 1;
    }
    if (pid > 0)
    {
        printf("parent Exiting and passing PID: %d\n", pid);
        _exit(0);
    }

    setsid();

    pid = fork();
    if (pid < 0)
    {
        printf("Could not fork() group leader: %d\n", errno);
        return 1;
    }
    if (pid > 0)
    {
        printf("Exiting and passing control to PID: %d\n", pid);
        _exit(0);
    }

    if (chdir("/") < 0)
    {
        printf("Unable to change working directory to / \n");
        return 1;
    }

    if (redirect_fd(STDIN_FILENO, O_RDONLY) || redirect_fd(STDOUT_FILENO, O_WRONLY) || redirect_fd(STDERR_FILENO, O_WRONLY))
    {
        printf("Unable to redirect standard file descriptors to /dev/null\n");
        return 1;
    }

    return 0;
}

int g_sck_can_recv(int sck, int millis)
{
    fd_set rfds;
    struct timeval time;
    int rv;

    memset(&time, 0x00, sizeof(time));
    time.tv_sec = millis / 1000;
    time.tv_usec = (millis * 1000) % 1000000;
    FD_ZERO(&rfds);

    if (sck > 0)
    {
        FD_SET(((unsigned int)sck), &rfds);
        rv = select(sck + 1, &rfds, 0, 0, &time);

        if (rv > 0)
        {
            return 1;
        }
    }

    return 0;
}

int create_socket()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        DEBUG(LOG_ERROR, "socket is create error %d\n", errno);
        return sockfd;
    }
    return sockfd;
}

int bind_server(int sockfd, int port)
{
    struct sockaddr_in client_addr;
    memset(&client_addr, 0x00, sizeof(client_addr));
    client_addr.sin_family = AF_INET;
    client_addr.sin_port = htons(port);
    client_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    int ret = bind(sockfd, (struct sockaddr *)&client_addr, sizeof(struct sockaddr));
    if (ret != 0)
    {
        DEBUG(LOG_ERROR, "bind is error %d\n", errno);
        return ret;
    }
    return 0;
}

int listen_server(int sockfd)
{
    int ret = listen(sockfd, 5);
    if (ret != 0)
    {
        DEBUG(LOG_ERROR, "listen is error %d\n", errno);
        return ret;
    }
    return 0;
}

int connect_server_handle(int port)
{
    int sock_fd = 0;
    struct sockaddr_in server_addr;

    sock_fd = socket(AF_INET, SOCK_STREAM, 0);

    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    int ret = connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
    if (ret != 0)
    {
        DEBUG(LOG_ERROR, "connect is failed, errno is [%d]", errno);
        return 0;
    }

    return sock_fd;
}
